import React, {forwardRef, Key, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState,} from 'react';
import {GTableProps} from './TableProps';
import {ActionType, ProTable} from '@ant-design/pro-components';
import {merge} from '../utils';
import {GTableSchema} from './TableSchema';
import {Button, message, Modal, Upload} from 'antd';
import {GIcon} from '../Icon';
import {GModalForm} from '../ModalForm';
import {RowSelectMethod} from 'antd/es/table/interface';
import {request} from "../Request";
import {getSchema} from "../utils/SchemaUtils";
import qs from 'qs';

export const GTable = forwardRef((props: GTableProps, ref: any) => {
  const [loading, setLoading] = useState<boolean>(false);
  const [schema, setSchema] = useState<GTableSchema>(props.schema!);
  const [originalFilter, setOriginalFilter] = useState<any>();
  const [filter, setFilter] = useState<any>();
  const [selection, setSelection] = useState<any>({
    selectedKeys: [],
    selectedRows: [],
    info: {type: 'multiple'},
  });
  const actionRef = useRef<ActionType>();

  const onRowSelectionChange = useCallback(
    (
      selectedKeys: Key[],
      selectedRows: any[],
      info: { type: RowSelectMethod },
    ) => {
      const newSelection = {
        selectedKeys: selectedKeys,
        selectedRows: selectedRows,
        info: info,
      };
      setSelection(newSelection);
      schema?.rowSelection?.onChange?.(selectedKeys, selectedRows, info);
      props.onSelected?.(newSelection);
    },
    [schema?.rowSelection?.onChange],
  );

  const fetchData = useCallback(
    async (params: any, sort: any, filter: any): Promise<any> => {
      if (schema?.data?.url) {
        const requestParam = {...params};
        requestParam.include = schema.data?.include;
        delete requestParam.current;
        const newFilter = {...requestParam};
        delete newFilter.pageSize;
        setFilter(newFilter);

        requestParam.pageIndex = params.current! - 1;

        const sortKeys = Object.keys(sort);
        if (sortKeys.length > 0) {
          requestParam.sort = sortKeys.map((key) => {
            return key + ':' + (sort[key] === 'ascend' ? 'asc' : 'desc');
          });
        } else {
          requestParam.sort = schema.data?.sort;
        }

        return request<any>(schema?.data?.url, {
          method: schema?.data?.method || 'get',
          requestData: requestParam,
          loadingSetter: setLoading,
          errMsg: '获取数据错误'
        }).then(resp => {
          const changedResp = props.onDataLoaded?.(resp);
          return {
            success: true,
            data: (changedResp ?? resp).content,
            total: (changedResp ?? resp).totalElements
          };
        });
      }
    },
    [schema]
  );

  const deleteData = useCallback((record: any[]) => {
    Modal.confirm({
      title: '确认',
      content: '确认删除数据?',
      onOk: () => {
        request(schema.data?.url!, {
          method: 'delete',
          data: record.map(r => {
            return schema?.action?.idGetter?.deleteId?.(r) || r[schema?.rowKey || 'id'];
          }),
          loadingSetter: setLoading,
          errMsg: '删除数据错误',
        }).then((resp) => {
          props.onDeleted?.(record);
          message.success('成功');
          actionRef.current?.reload();
          actionRef.current?.clearSelected();
        });
      }
    });
  }, [schema]);

  const buttonAdd = useMemo(() => {
    if (schema?.action?.toolbar?.add) {
      const buttonProps = merge(
        {
          button: {
            text: '新建',
            button: {
              icon: <GIcon type="icon-plus"/>,
              type: 'primary',
            }
          },
          form: {
            schema: {
              data: {
                url: schema?.data?.url
              }
            }
          }
        },
        schema.action.toolbar.add === true ? null : schema.action.toolbar.add,
        {
          form: {
            onSubmitted: (value: any, resp: any) => {
              message.success('成功');
              actionRef.current?.reload();
              schema?.action?.toolbar?.add?.form?.onSubmitted?.(value, resp);
            }
          }
        }
      );
      return (
        <GModalForm
          {...buttonProps}
        />
      )
    } else {
      return null;
    }
  }, [schema]);

  const buttonDeleteMulti = useMemo(() => {
    return schema?.action?.toolbar?.delete ? (
      <Button
        icon={<GIcon type="icon-delete"/>}
        danger
        type="primary"
        disabled={selection.selectedKeys.length === 0}
        onClick={() => {
          deleteData(selection.selectedRows);
        }}
      >
        删除
      </Button>
    ) : null;
  }, [selection, schema?.action?.toolbar?.delete]);

  const buttonExcelExport = useMemo(() => {
    return schema?.action?.toolbar?.excelExport ? (
      <Button
        icon={<GIcon type="icon-download"/>}
        onClick={() => {
          const requestParams = filter || originalFilter || schema?.data?.filter;
          const query = qs.stringify(requestParams, {indices: false})
          window.open(schema?.data.url + '/excel?' + query, 'blank');
        }}
        {...schema.action.toolbar.excelExport}
      >导出</Button>
    ) : null
  }, [schema, filter]);

  const buttonExcelImport = useMemo(() => {
    return schema?.action?.toolbar?.excelImport ? (
      <Upload
        showUploadList={false}
        action={schema.data.url + '/excel'}
        onChange={(info) => {
          if (info.file.status === 'done') {
            actionRef.current?.reload();
            message.success(`导入成功`);
          } else if (info.file.status === 'error') {
            message.error(`导入失败`);
          }
        }}
      >
        <Button
          icon={<GIcon type="icon-upload"/>}
          {...schema.action.toolbar.excelImport}
        >导入</Button>
      </Upload>
    ) : null
  }, [schema]);

  const createButtonEdit = useCallback((record: any) => {
    const id = schema?.action?.idGetter?.editId?.(record) || record[schema?.rowKey || 'id'];
    const buttonProps = merge(
      {
        button: {
          text: '编辑',
          button: {
            type: "link",
            size: "small"
          }
        },
        form: {
          onSubmitted: () => {
            message.success('成功');
            actionRef.current?.reload();
          },
          schema: {
            data: {
              autoload: true,
              urlLoad: schema?.data?.url + '/one',
              url: schema?.data?.url,
              filter: {id: id}
            }
          },
        }
      },
      schema?.action?.toolbar?.add?.form ? {
        form: schema.action.toolbar.add.form
      } : null,
      {
        form: {
          schema: {
            data: {
              methodSubmit: 'put'
            }
          }
        }
      },
      schema?.action?.row?.edit === true ? null : schema?.action?.row?.edit,
      {
        form: {
          onSubmitted: (value: any, resp: any) => {
            message.success('成功');
            actionRef.current?.reload();
            schema?.action?.toolbar?.edit?.form?.onSubmitted ?
              schema.action.toolbar.edit.form.onSubmitted(value, resp)
              :
              schema?.action?.toolbar?.add?.form?.onSubmitted?.(value, resp)
          }
        }
      }
    );
    return (
      <GModalForm
        key={"edit-" + id}
        {...buttonProps}
      />
    )
  }, [schema]);

  const createButtonDelete = useCallback((record: any) => {
    return (
      <Button
        key={'delete-' + record[schema?.rowKey || 'id']}
        type="link"
        danger
        size="small"
        onClick={() => {
          deleteData([record]);
        }}
        {...schema.action.row.delete}
      >
        {schema.action.row.delete.text || '删除'}
      </Button>
    )
  }, [schema]);

  const rowOperation = {
    title: '操作',
    valueType: 'option',
    key: 'option',
    render: (text: any, record: any) => [
      (schema?.action?.row.edit || schema?.action?.row.editVisible) && (!schema?.action?.row.editVisible || schema?.action?.row.editVisible?.(record)) ? createButtonEdit(record) : null,
      (schema?.action?.row.delete || schema?.action?.row.deleteVisible) && (!schema?.action?.row.deleteVisible || schema?.action?.row.deleteVisible?.(record)) ? createButtonDelete(record) : null,
      schema?.action?.row?.render?.(record),
    ],
  };

  useImperativeHandle(
    ref,
    () => ({
      selection: selection,
      loadData: (filter?: any) => {
        setOriginalFilter(filter);
        actionRef.current?.reload();
      },
    }),
    [selection]
  );

  useEffect(() => {
    if (props.schemaPath) {
      getSchema<GTableSchema>(props.schemaPath, setLoading)
        .then(remoteSchema => {
          remoteSchema.columns?.forEach((column: any) => {
            if (column.render) {
              column.render = eval(column.render);
            }
          });
          setSchema((schema: GTableSchema) => {
            return merge(schema, remoteSchema);
          });
        });
    }
  }, [props.schemaPath]);

  return (
    <ProTable<any>
      actionRef={actionRef}
      rowKey="id"
      defaultSize="large"
      loading={loading}
      params={originalFilter || schema?.data?.filter}
      request={fetchData}
      search={schema?.search || false}
      tableAlertRender={false}
      {...schema}
      toolBarRender={(action, rows) => [
        buttonAdd,
        buttonDeleteMulti,
        buttonExcelExport,
        buttonExcelImport
      ].concat(schema?.toolBarRender?.(action, rows))}
      columns={
        schema?.action?.row?.edit || schema?.action?.row?.delete || schema?.action?.row?.render ?
          schema?.columns?.concat({
            ...rowOperation,
            ...schema?.action?.row?.props,
          })
          :
          schema?.columns
      }
      rowSelection={
        schema?.action?.toolbar?.delete || schema?.rowSelection
          ? {
            ...schema?.rowSelection,
            onChange: onRowSelectionChange,
          }
          : undefined
      }
      pagination={{
        defaultPageSize: 10,
        showSizeChanger: true,
        ...schema?.pagination,
      }}
    />
  );
});
